#include <QSqlRecord>
#include <QSqlQuery>
#include <QDebug>
#include <QSqlError>
#include <QVariant>
#include "ccuisinemodel.h"
#include "cconstdefinition.h"
#include "cutility.h"

using namespace cooking;


const QString CCuisineModel::KCuisineRoleNamesMap[][2]= {
    {KCuisineId, KTableCuisine + KDot + KCuisineId},
    {KCuisineParentId, KTableCuisine + KDot + KCuisineParentId},
    {KCuisineName, KTableCuisine + KDot + KCuisineName},
    {KCuisineRegionId, KTableCuisine + KDot + KCuisineRegionId},
    {KCuisineDescription, KTableCuisine + KDot + KCuisineDescription},
    {KCuisinePicture, KTableCuisine + KDot + KCuisinePicture},
    {KCuisineRecipeCount, KNullString},
    {KNullString,KNullString}
};

CCuisineModel::CCuisineModel(CEngine& engine, QObject *parent) :
    CModel(engine, parent),m_parentId(-1), m_roleStart(-1), m_roleEnd(0) {
    initRoles();
    refresh();
}

CCuisineModel::CCuisineModel(int parentId, CEngine& engine, QObject *parent) :
    CModel(engine, parent), m_parentId(parentId), m_roleStart(-1), m_roleEnd(-1) {
    initRoles();
    refresh();
}

QString CCuisineModel::queryString() {
    QString queryString(KSelect);
    int i = 0;
    bool roleAdded = false;
    while (KCuisineRoleNamesMap[i][0] != KNullString) {
        // only add real columns, recipeCount is not real column
        if (KCuisineRoleNamesMap[i][1] != KNullString) {
            //add comma if role added and not the first one.
            if (i != 0 && roleAdded) {
                queryString += KComma;
            }
            queryString += KCuisineRoleNamesMap[i][1];
            roleAdded = true;
        }
        ++i;
    }
    //use left join because cuisine may do not have picture
    queryString += KFrom + KTableCuisine;
    queryString += KWhere + KTableCuisine + KDot + KCuisineParentId
            + KEqual + QString::number(m_parentId);
    qDebug()<<"query string:"<<queryString;
    return queryString;
}

void CCuisineModel::setParentId(int parentId) {
    if (m_parentId != parentId) {
        beginResetModel();
        m_parentId = parentId;
        refresh();
        emit parentIdChanged();
        endResetModel();
    }
}

int CCuisineModel::parentId() {
    return m_parentId;
}

void CCuisineModel::initRoles() {
    QHash<int, QByteArray> roles;
    int index = 0;

    while (KCuisineRoleNamesMap[index][0] != KNullString) {
        roles[index + KCuisineRoleStart] = KCuisineRoleNamesMap[index][0].toLocal8Bit();
        ++index;
    }
    m_roleStart = KCuisineRoleStart;
    m_roleEnd = index + KCuisineRoleStart - 1;
    setRoleNames(roles);
}

void CCuisineModel::refresh() {
    setQuery(queryString());
}

int CCuisineModel::rowCount(const QModelIndex& parent) const {
    int count = QSqlQueryModel::rowCount(parent);
    qDebug()<<"rowCount:"<<count;
    return count;
}

QVariant CCuisineModel::data(const QModelIndex& index, int role) const {
    QVariant value;
    if (role >= m_roleStart && role <= m_roleEnd ) {
        // recipe count need to query recipe table
        if (KCuisineRoleNamesMap[role - m_roleStart][0] == KCuisineRecipeCount) {
            int cuisineId = getSelectCuisineId(index);
            // query recipe count which's cuisine id, parent id, ancestor id equal cuisineId
            QList<int> *childrenIds = new QList<int>();
            childrenCuisineIds(cuisineId, *childrenIds, true);

            QString queryStr = QString(KSelect + KCountAll + KFrom + KTableCuisineRecipe
                                       + KWhere + KCuisineRecipeCuisineId + KIn
                                       + "(" );
            queryStr += QString(cuisineId);
            for (int i = 0; i < childrenIds->size(); i++) {
                // add id to query string
                queryStr += QString::number(childrenIds->at(i));
                // not the last one, always add ','
                if (i != childrenIds->size() - 1) {
                    queryStr += KComma;
                }
            }
            delete childrenIds;
            queryStr += ")";
            qDebug()<<"query string"<<queryStr;
            QSqlQuery recipeQuery(queryStr);
            if (recipeQuery.next()) {
                value = recipeQuery.value(0);
            }
            else {
                // no value, return 0
                value.fromValue(0);
            }
        }
        else {
            int column = role - KCuisineRoleStart;
            QModelIndex roleIndex = this->index(index.row(), column);
            value = QSqlQueryModel::data(roleIndex);
        }
    }
    else {
        qDebug()<<"invalid role:"<<role<<" valid:["<<m_roleStart<<"-"<<m_roleEnd<<"]";
    }

    return value;
}

int CCuisineModel::getSelectCuisineId(const QModelIndex& index) const {
    int id = CUtility::getRoleId(KCuisineRoleNamesMap, m_roleStart, KCuisineId);
    int column = id - KCuisineRoleStart;
    QModelIndex cuisineIdIndex = this->index(index.row(), column);
    QVariant cuisineId = QSqlQueryModel::data(cuisineIdIndex);
    qDebug()<<"cuisineRoleId:"<<id
            <<" column:"<<column
            <<" cuisineId"<<cuisineId;
    return cuisineId.toInt();
}

void CCuisineModel::childrenCuisineIds(int parentId, QList<int>& childrenIds, bool recursive) const {
    if (recursive) {
        QList<int> directChildren;
        directChildrenIds(parentId, directChildren);
        for (int i = 0; i < directChildren.size(); i++) {
            //append direct child
            childrenIds.append(directChildren[i]);
            //recursive query indirect children
            childrenCuisineIds(directChildren[i], childrenIds, true);
        }
    }
    else {
        directChildrenIds(parentId, childrenIds);
    }
}

void CCuisineModel::directChildrenIds(int parentId, QList<int>& childrenIds) const {
    QString queryStr = QString(KSelect + KCuisineId + KFrom + KTableCuisine
                               + KWhere + KCuisineParentId + KEqual + QString(parentId));
    qDebug()<<"query string"<<queryStr;
    QSqlQuery recipeQuery(queryStr);
    while (recipeQuery.next()) {
        childrenIds.append(recipeQuery.value(0).toInt());
    }
}
